#ifndef __SEMANTIC__
#define __SEMANTIC__

#include "Util.h"
#include "ParserTreeNode.h"
#include <fstream>
#include <cstdlib>
#include <vector>

typedef enum {NUMBER_VALUE, LITERAL_VALUE, LIST_VALUE, FUNC_VALUE, NOTHING} ValueType;


class Value
{
public:
	Value(){type = NOTHING; list = NULL;};
	Value(const Value& value)
	{
		type = value.type;
		n = value.n;
		str = value.str;
		if (!value.list)
			list = NULL;
		else
			list = value.list->copy();
	}
	Value& operator=(const Value& value) 
	{
		type = value.type;
		n = value.n;
		str = value.str;
		if (!value.list)
			list = NULL;
		else
			list = value.list->copy();
		return *this;
	}
	~Value()
	{
		delete list;
	}
	ValueType type;
	int n;				//	if type is NUMBER, use this value
	std::string str;		//	if type is LITERAL or FUNC, use this value
	List* list;			//	if type is LIST, use this value

	bool isList(){return type == LIST_VALUE || isNil();}						//	list or NIL
	bool isAtom(){return type == NUMBER_VALUE || type == LITERAL_VALUE || isNil();}			//	atom or NIL
	bool isNil(){return ((type == LITERAL_VALUE && Util::isEqual(str, "NIL"))			//	NIL atom or ()
		|| (type == LIST_VALUE && list->isDOT() && !list->getChild(true)));}
	bool isT(){return type == LITERAL_VALUE && Util::isEqual(str, "T");}
	bool isNumber(){return type == NUMBER_VALUE;}
	bool isLiteral(){return type == LITERAL_VALUE;}

	void setNil(){type = LITERAL_VALUE; str = "NIL";}
	void setT(){type = LITERAL_VALUE; str = "T";}
	void setLiteral(std::string l){type = LITERAL_VALUE; str = l;}
	void setFunc(std::string l){type = FUNC_VALUE; str = l;}
	void setNumber(int number){type = NUMBER_VALUE; n = number;}
	void setList(List* l){type = LIST_VALUE; list = l;}

	// print to output file
	void print()
	{
		switch(type)
		{
		case NUMBER_VALUE:
			std::cout << n ;
			break;
		case LITERAL_VALUE:
			std::cout << Util::toUpperCase(str).c_str();
			break;
		case LIST_VALUE:
			setTreeType(list);
			printList(list);
			break;
		case FUNC_VALUE:
			std::cout << Util::toUpperCase(str).c_str();
			break;
		}
	}
	
	TreeType setTreeType(List* list)
	{
		if (list)
		{
			if (!list->isDOT())
			{
				if (list->isNIL())
					list->treeType = NIL_TREE;
				else
					list->treeType = ATOM_TREE;
			}
			else
			{
				if (!list->getChild(true))
					list->treeType = NIL_TREE;
				else 
				{
					setTreeType(list->getChild(true));
					switch(setTreeType(list->getChild(false)))
					{
					case NONE_TREE: 
						list->treeType = LIST_TREE;
						break;
					case NIL_TREE: 
						list->treeType = LIST_TREE;
						break;
					case ATOM_TREE: 
						list->treeType = DOT_TREE;
						break;
					case DOT_TREE: 
						list->treeType = DOT_TREE;
						break;
					case LIST_TREE: 
						list->treeType = LIST_TREE;
						break;
					}
				}
			}
			return list->treeType;
		}
		else
			return NONE_TREE;

	}

	// print to output file
	void printList(List* list)
	{
		if (list)
		{
			switch(list->treeType)
			{
			case NONE_TREE:
				break;
			case NIL_TREE:
				std::cout << "NIL";
				break;
			case ATOM_TREE:
				if (list->isNumber())
					std::cout << list->getAtom().number;
				else
					std::cout<< Util::toUpperCase(list->getAtom().str).c_str();
				break;
			case DOT_TREE:
				std::cout << "(";
				printList(list->getChild(true));
				std::cout << " . ";
				printList(list->getChild(false));
				std::cout << ")";
				break;
			case LIST_TREE:
				std::cout << "(";
				while (true)
				{
					if (list->getChild(true))
						printList(list->getChild(true));
					
					if (list->getChild(false) && list->getChild(false)->treeType != NIL_TREE)
					{
						std::cout << " ";
						list = list->getChild(false);
					}
					else
						break;

					
				}
				std::cout << ")";
				break;
			}
		}
	}
};

//	declare function
class Function
{
public:
	Function(){name = ""; formal = NULL; body = NULL;};
	Function(std::string funcName, List* funcFormal, List* funcBody)
	{
		name = funcName;
		if (!funcFormal)
			formal = NULL;
		else
			formal = funcFormal->copy();
		if (!funcBody)
			body = NULL;
		else
			body = funcBody->copy();		
	}	
	Function(const Function& function)
	{
		name = function.name;
		if (!function.formal)
			formal = NULL;
		else
			formal = function.formal->copy();
		if (!function.body)
			body = NULL;
		else
			body = function.body->copy();
	}
	Function& operator=(const Function& function) 
	{
		name = function.name;
		if (!function.formal)
			formal = NULL;
		else
			formal = function.formal->copy();
		if (!function.body)
			body = NULL;
		else
			body = function.body->copy();
		return *this;
	}
	~Function()
	{
		delete formal;
		delete body;
	}
	std::string name;	//	function name
	List* formal;		//	function formal parameters
	List* body;		//	function body
};


//	declare variable
class Variable
{
public:
	std::string name;	//	variable name
	Value value;		//	variable value

	Variable(std::string varName, Value& varValue)
	{
		name = varName;
		value = varValue;
	}
};

//	declared variable list
class AList
{
private:
	std::vector<Variable> variables;
public:
	AList(){};
	~AList(){};
	void addVariable(Variable variable)
	{
		for (std::vector<Variable>::size_type i = 0; i < variables.size(); i++)
			if (Util::isEqual(variables[i].name, variable.name))
			{
				variables.erase(variables.begin() + i);
			}
		variables.push_back(variable);
	}
	bool findVariable(std::string varName)
	{
		for (std::vector<Variable>::size_type i = 0; i < variables.size(); i++)
		{
			if (Util::isEqual(variables.at(i).name, varName))
				return true;
		}
		return false;
	}
	Value getVariableValue(std::string varName)
	{
		for (std::vector<Variable>::size_type i = 0; i < variables.size(); i++)
		{
			if (Util::isEqual(variables.at(i).name, varName))
				return variables.at(i).value;
		}
		return Value();
	}
};


//	declared function list
class DList
{
private:
	std::vector<Function> functions;

public:
	DList(){};
	~DList(){};
	void addFunction(Function function)
	{
		for (std::vector<Function>::size_type i = 0; i < functions.size(); i++)
			if (Util::isEqual(functions[i].name, function.name))
			{
				functions.erase(functions.begin() + i);
			}
		functions.push_back(function);
	}
	bool findFunction(std::string funcName)
	{
		for (std::vector<Function>::size_type i = 0; i < functions.size(); i++)
		{
			if (Util::isEqual(functions.at(i).name, funcName))
				return true;
		}
		return false;
	}
	ParserTree* getFunctionFormal(std::string funcName)
	{
		for (std::vector<Function>::size_type i = 0; i < functions.size(); i++)
		{
			if (Util::isEqual(functions.at(i).name, funcName))
				return functions.at(i).formal;
		}
		return NULL;
	}
	ParserTree* getFunctionBody(std::string funcName)
	{
		for (std::vector<Function>::size_type i = 0; i < functions.size(); i++)
		{
			if (Util::isEqual(functions.at(i).name, funcName))
				return functions.at(i).body;
		}
		return NULL;
	}
};

enum Arith {PLUS, MINUS, TIMES, QUOTIENT, REMAINDER, EQ, LESS, GREATER};

class Semantic
{
public:
	Semantic(void);
	~Semantic(void);

public:
	void eval(ParserTree* tree, Value& value, AList alist);	//	evaluate a parser tree and return the value

private:
	DList dList;

public:
	// car result of a list
	void car(ParserTree* tree, Value& value, AList alist);
	// cdr result of a list
	void cdr(ParserTree* tree, Value& value, AList alist);
	// check if is atom
	void atom(ParserTree* tree, Value& value, AList alist);
	// check if is null
	void null(ParserTree* tree, Value& value, AList alist);
	// check if is int
	void integer(ParserTree* tree, Value& value, AList alist);
	// evaluate and print the result
	void printValue(ParserTree* tree, AList alist);
	// quote result of a list
	void quote(ParserTree* tree, Value& value, AList alist);
	// arithmetic
	void arith(ParserTree* tree, Value& value, AList alist, Arith arith);
	// cons
	void cons(ParserTree* tree, Value& value, AList alist);
	// cond
	void cond(ParserTree* tree, Value& value, AList alist);
	//  cond branch execution, return whether this branch executed.
	bool condBranch(ParserTree* tree, Value& value, AList alist);
	// defun
	void defun(ParserTree* tree, Value& value, AList alist);
	//	evaluate values of a list
	void evalList(ParserTree* tree, Value& valueList, AList alist);
	//	evaluate a call to user delcared function
	void apply(std::string funcName, Value& value, Value& valueList, AList alist);
};

#endif
